#pragma once
#include <core/container/LinkedHashMap.h>
#include <core/container/SHash.h>
#include <core/strings/TinyString.h>

#include <map>
#include <string>

#include "bondlib.h"
/*
        [内存优化][scofined.qi]
        BondContainer中以数组的形式保存市场上全部债券的信息,每支债券都有一个
        唯一的主键CombBondKey,即CBondInfoImpl.m_sCombBondKey
        为了快速通过主键来搜索债券,CBondContainer中用容器m_keymap来保持
        从主键CombBondKey到数组索引的映射
        当前文件就是这个容器的内存优化的改进实现

        绝大部分债券主键是存在严格编码规律的,BondCodeCompact就是从编码规律中
        提取的结构体,BondKeyParser用来从字符串形式的债券主键解码成BondCodeCompact,
        以便很好的在从BondCodeCompact到int的哈希映射中快速查找

        债券代码也是类似的,BondCodeCompact表示从编码规律提取的结构体
        BondCodeParser是如何从字符串形式的债券代码解析到BondCodeCompact

        少量无规律,无法用BondCodeCompact编码的债券主键,则以
        std::map<kstring, BondIndex> m_backupmap的形式存在,只占2k左右
        BondCodeCompact中,将债券索引先定位6+16个比特位,即最大支持4194304
        足够支撑现有债券发展趋势

        使用新的容器的好处:
        1.编码内存少:只需要10个字节,就能描述主键key和债券索引的信息
        2.占用内存少:原先哈希表一个节点只能放一个键值,节点中元信息内存比例较高
                                 新容器中,一个节点至多放16个键值,节点中源信息内存比例较低
        3.内存碎片少:所有节点都是固定大小
        4.缓存行友好:哈希碰撞的所有主键都放在一个节点中,落在紧凑的缓存行中
        5.并行搜索  :在一个节点中,使用硬加速的并行搜索指令进行快速过滤

        简单的说,就是内存少,碎片少,性能快
        要注意的是:
                1.债券总数量限定为2^22个
                2.债券主键绝大部分符合提取的编码规律
                3.经过测试,该哈希表的哈希插槽数组长度为2^13,性能和内存综合最好
                        因此BondKeyMapImpl中,Expand的参数为13,表示长度为2^13
*/
typedef intptr_t BondIndex;
typedef unsigned int uint;
typedef int64_t i64;
#pragma pack(push)
#pragma pack(2)

#ifdef WIN32
#define KEY_ALIGN __declspec(align(1))
#else
#define KEY_ALIGN __attribute__((aligned(1)))
#endif
struct KEY_ALIGN ComBondKeyCompact {  // 将债权键解析出每个字段

  enum {
    LowBitMask = (1 << 26) - 1,
    HashSize = sizeof(uint) + sizeof(uint),
    kInvalidHead = 0,  // 0表示bkey的head空值
  };
  struct First {
    uint id : 16;    // 最大值:1706
    uint seq : 14;   // 最大值:10302
    uint nzero : 2;  // 2位,
  };
  struct Second {
    uint head : 5;    // 5bit,A-Z,如果是小写都转化为大写
    uint year : 5;    // 32年,从2000开始
    uint market : 5;  // 5bit,总共有6种,支持32种
    uint type : 5;    // 4bit,总共有12中,支持64
    uint sub : 6;     // 6bit,总共有38种,支持64
    uint hibid : 6;   //
  };
  union {
    First first;
    uint ufirst;
  };
  union {
    Second second;
    uint usecond;
  };
  unsigned short bid;  //
 public:
  ComBondKeyCompact() {
    Clear();
    static_assert(sizeof(ComBondKeyCompact) == 10, "sizeof(ComBondKeyCompact)");
  }
  void Clear() { memset(this, 0, sizeof(*this)); }
  bool IsEmpty() const { return second.head == kInvalidHead; };
  unsigned int GetHash() const {
    // 要求hibid必须为0
    return qb::base::murMurHash((const char*)this, HashSize);
  }
  bool EqualKey(const ComBondKeyCompact& key) const {
    // 忽略key的bid和hibid,检查两个结构体是否相同
    // 可以考虑用加速函数如_mm_cmpeq_epi8
    return ufirst == key.ufirst &&
           (usecond & LowBitMask) == (key.usecond & LowBitMask);
  };
  int GetValue() const { return ((uint)second.hibid) << 16 | ((uint)bid); }
  void SetValue(const BondIndex& value) {
    bid = ((unsigned int)value & 0x0000ffff);
    second.hibid = ((unsigned int)value) >> 16;
  };
  bool operator<(const ComBondKeyCompact& key) const {
    if (second.year == key.second.year) return *((i64*)this) < *((i64*)&key);
    return second.year < key.second.year;
  }
};
#pragma pack(pop)

struct BondCodeCompact {  // 总共也是10个字节
  enum { LOW_MASK = 0x000003FF };
  struct First {
    unsigned int id : 22;
    unsigned int code : 5;  // A-Z 26个字符,0表示没有
    unsigned int seq : 5;  // 不超过32的两位小数,不能以0开头,不能是0
  };
  struct Second {
    unsigned short flag : 1;    // 0表示纯数字模式,1表示混合模式
    unsigned short zero : 3;    // 最前面有几个数字0
    unsigned short market : 6;  // .IB,.SH,.SZ等
    unsigned short hibid : 6;   //
    unsigned short bid;         // 共22位
  };
  union {
    First first;      // 当flag为0时取这个,表示有code和seq区分
    unsigned int id;  // 当flag为1时取这个,表示纯数字
  };
  union {
    Second second;
    unsigned int index;
  };
  BondCodeCompact() {
    Clear();
    static_assert(sizeof(BondCodeCompact) == 8, "sizeof(ComBondKeyCompact)");
  }
  void Clear() {
    id = 0;
    index = 0;
  }
  bool IsEmpty() const { return id == 0; };
  unsigned int GetHash() const {
    // 要求hibid必须为0
    BondCodeCompact info;
    info.id = id;
    info.index = index;
    info.second.bid = 0;
    info.second.hibid = 0;
    return qb::base::murMurHash((const char*)&info, sizeof(BondCodeCompact));
  }
  bool EqualKey(const BondCodeCompact& key) const {
    // 忽略key的bid和hibid,检查两个结构体是否相同
    // 可以考虑用加速函数如_mm_cmpeq_epi8
    return second.flag == key.second.flag && second.zero == key.second.zero &&
           id == key.id && second.market == key.second.market;
  };
  int GetValue() const {
    return ((uint)second.hibid) << 16 | ((uint)second.bid);
  }
  void SetValue(const BondIndex& value) {
    second.bid = ((unsigned int)value & 0x0000ffff);
    second.hibid = ((unsigned int)value) >> 16;
  };
  bool operator<(const BondCodeCompact& key) const {
    if (id == key.id) {
      return (index & LOW_MASK) < (key.index & LOW_MASK);
    }
    return id < key.id;
  }
};
struct CodeId {
  union {
    char code[4];
    int id;
  };

 public:
  CodeId(const char* str) : id(0) {
    code[0] = str[0];
    code[1] = str[1];
    code[2] = str[2];
    code[3] = '\0';
  }
  CodeId(int d) : id(d) {}
  inline bool operator<(const CodeId& ci) const { return id < ci.id; }
  inline bool EqualTo(const char* str) const {
    return code[0] == str[0] && code[1] == str[1] && code[2] == str[2];
  }
};
class NameDict {
  // 后续可以考虑用哈希表实现
  struct CodeIdInfo {
    CodeId code;
    int index;

   public:
    CodeIdInfo(const char* str) : code(str), index(0) {}
    CodeIdInfo() : index(0), code(0) {}
    void Clear() { index = -1, code = CodeId(0); }
    bool IsEmpty() const { return index < 0; };
    unsigned int GetHash() const { return code.id; }
    bool EqualKey(const CodeIdInfo& key) const {
      return code.id == key.code.id;
    };
    const int& GetValue() const { return index; }
    void SetValue(const int& value) { index = value; };
  };
  typedef qb::base::LinkedHashMap<CodeIdInfo, int> CodeDict;
  CodeDict m_dict;
  CodeIdInfo** m_array;
  int m_capcity;
  int m_count;

 public:
  NameDict(int capcity = 100) : m_dict(capcity), m_count(0) {
    m_array = new CodeIdInfo*[capcity];
    memset(m_array, 0, capcity * sizeof(CodeIdInfo*));
  }
  ~NameDict() {}
  int GetCount() const { return m_dict.GetCount(); }
  int GetBytes() const { return m_dict.GetBytes(); }
  int Add(const char* str) {
    CodeIdInfo info(str);
    if (m_dict.Get(info, info.index)) return info.index;
    int index = m_count;
    m_dict.Put(info, index);
    m_count++;
    return index;
  }
};

class S_BONDLIB_EXPORT NameDictHash {
  // 后续可以考虑用哈希表实现
  struct NodeInfo {
    CodeId code;
    int index;
    NodeInfo* next;
    NodeInfo(NodeInfo* n, int i, const CodeId& ci)
        : index(i), next(n), code(ci.id) {}
    NodeInfo(NodeInfo* n, int i, const char* str)
        : index(i), next(n), code(str) {}
  };
  NodeInfo** m_array;
  // map<CodeId, int>	m_codes;
  int m_capcity;
  int m_bitcount;
  int m_count;

 public:
  NameDictHash(int capcity = 4, const char* str = nullptr);
  ~NameDictHash();
  void Init(const char* str);
  int GetCount() const { return m_count; }
  int GetBytes() const { return m_count * sizeof(NodeInfo) + sizeof(*this); }
  int Add(int id);
  int Add(const char* str);
};
typedef NameDictHash DictType;

class S_BONDLIB_EXPORT BondKeyParser {
  typedef ComBondKeyCompact ComBondKey;
  DictType m_types;    // 12,4bit
  DictType m_subs;     // 38,6bit
  DictType m_markets;  // 6,3bit
 public:
  BondKeyParser();
  bool Parse(const char* str, int len, ComBondKey& key);
  bool IsAlpha(char c) const {
    return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
  }

  int ToInt(const char* str, int pos, int len) {
    int num = 0;
    const char* ptr = str + pos;
    const char* end = ptr + len;
    while (ptr < end) {
      num = (num << 3) + (num << 1) + (*ptr++ - '0');
    }
    return num;
  }
  int GetIntLength(const char* str, int len) const;
  int GetTypeRange(const char* str, int len, int& end);
};
class S_BONDLIB_EXPORT BondCodeParser {
  enum {
    MaxSeq = 31,  // 2^5-1
    MaxCode = 26,  // 0,'A'对应编码1,'B'对应编码2,'C'对应编码3,...'Z'对应编码26
    MaxZero = 7,  // 最前面'0'的最大个数为7,只有3个编码位
    MaxShortId = 1 << 22,  // 22个比特位表示
  };
  DictType m_markets;  // 6,3bit
 public:
  BondCodeParser();
  bool Parse(const char* str, int len, BondCodeCompact& code);
  int TestNum(const char* str, int len, int& number) {
    int i = 0;
    number = 0;
    for (; i < len; i++) {
      if (str[i] < '0' || str[i] > '9') break;
      number = number * 10 + (str[i] - '0');
    }
    return i;
  }
  int TestZero(const char* str, int len) {
    int i = 0;
    for (; i < len; i++) {
      if (str[i] != '0') break;
    }
    return i;
  }
};
template <typename EntryType, typename KeyParser,
          typename CacheLine = qb::base::F14LinkedChunk<EntryType, BondIndex> >
class BondKeyHashMap {  // 专门用于债券组合键的哈希表
  typedef unsigned short ushort;
  typedef unsigned int uint;
  typedef int64_t i64;

  static_assert(sizeof(i64) == 8, "");
  typedef qb::base::TinyString
      KeyType;  // 比KeyString,在查找的时候,减少一次内存分配
  typedef std::map<KeyType, BondIndex> IndexMap;
  // 示例值:G0001242014ABSAMP0801.CIB
  // 假设:不存在G124的发行人编号

 public:
  enum {
    kMaxBondId =
        1 << 22,  // 400w债券,这个值和kYearBase根bkey中的year占的位个数有关
  };
  struct ComBondKeyBig {  // 将债权键解析出每个字段
    char head;            // 第一个字母,表示发行人编号的字母
    char type;            //
    char sub;             //
    char market;          //
    ushort id;            // 发行人编号数字
    ushort nzero : 2;  // 编号,高2bit表示前面有0的个数,最大支持4个
    ushort seq : 14;  // 编号,低0-14位,表示转化的整数,最大值为16384
    ushort year : 10;  // 表示年份
    ushort hibid : 6;  // 额外6个bit表示bondindex,总共22个,支持400w
    ushort bid;        // bond index,bid不参与hash计算
   public:
    ComBondKeyBig() { Clear(); }
    inline void Clear() { memset(this, 0, sizeof(*this)); }
    inline bool IsEmpty() const {
      return head == ComBondKeyCompact::kInvalidHead;
    };
    inline unsigned int GetHash() const {
      // TODO: security in x64?
      return qb::base::murMurHash((const char*)this,
                                  sizeof(ComBondKeyBig) - sizeof(ushort));
    }
    inline bool EqualKey(const ComBondKeyBig& key) const {
      // 忽略key的bid和hibid,检查两个结构体是否相同
      // 可以考虑用加速函数如_mm_cmpeq_epi8
      return *((i64*)this) == *((i64*)&key) && year == key.year;
    };
    inline int GetValue() const { return ((uint)hibid) << 16 | ((uint)bid); }
    inline void SetValue(const BondIndex& value) {
      bid = ((unsigned int)value & 0x0000ffff);
      hibid = ((unsigned int)value) >> 16;
    };
    inline bool operator<(const ComBondKeyBig& key) const {
      if (year == key.year) return *((i64*)this) < *((i64*)&key);
      return year < key.year;
    }
  };
  // typedef qb::base::HighResTime HighResTime;
  typedef EntryType ComBondKey;
  typedef qb::base::LinkedHashMap<ComBondKey, BondIndex, CacheLine> BondKeyMap;
  // typedef LinkedHashMap<ComBondKey, BondIndex, CacheLineArray<ComBondKey,
  // BondIndex>> BondKeyMap;
 private:
  KeyParser m_parser;
  BondKeyMap m_bondkeys;  //
  IndexMap m_backupmap;   // 备用表,适合解析失败的bondkey
  double m_update;        //
 public:
  BondKeyHashMap() : m_bondkeys(), m_update(0) {}
  ~BondKeyHashMap() {}
  void RemoveAll() {
    m_backupmap.clear();
    BondKeyMap temp(m_bondkeys.GetBitCount());
    m_bondkeys.Swap(temp);
  }
  BondIndex GetBondIndex(const char* comBondKey, int len) {
    ComBondKey key;
    if (m_parser.Parse(comBondKey, len, key) && !key.IsEmpty()) {
      BondIndex bid = -1;
      if (m_bondkeys.Get(key, bid)) return bid;
    } else {
      auto it = m_backupmap.find(qb::base::TempTinyString(comBondKey));
      if (it != m_backupmap.end()) return it->second;
    }
    return -1;
  }
  void AddBondKey(const char* comBondKey, int len, BondIndex bid) {
    ComBondKey key;
    // HighResTime timer;
    if (m_parser.Parse(comBondKey, len, key) && !key.IsEmpty() &&
        bid < kMaxBondId)
      m_bondkeys.Put(key, bid);
    else
      m_backupmap.emplace(KeyType(comBondKey, len), bid);
    // m_update += timer.elapsed();
  }
  bool EraseBondKey(const char* comBondKey, int len) {
    ComBondKey key;
    if (m_parser.Parse(comBondKey, len, key) && !key.IsEmpty())
      return m_bondkeys.Erase(key);
    else {
      auto it = m_backupmap.find(KeyType(comBondKey, len));
      if (it != m_backupmap.end()) {
        m_backupmap.erase(it);
        return true;
      }
    }
    return true;
  }
  void Expand(int capBits) { m_bondkeys.Expand(capBits); }
  int GetCount() const { return m_bondkeys.GetCount() + m_backupmap.size(); }
  int GetLength() const { return m_bondkeys.GetCount() + m_backupmap.size(); }
  int GetBytes() const {
    qb::base::LinkedHashMapStat stat;
    m_bondkeys.GetStat(stat);
    return stat.bytes;
  }
  double GetLoadRatio() const { return m_bondkeys.GetLoadRatio(); }

 public:  // 接口适配
  void emplace(const std::string& str, int bid) {
    AddBondKey(str.c_str(), str.size(), bid);
  }
  class iterator {
    int m_bondid;

   public:
    iterator(int bid = -1) : m_bondid(bid) {}
    bool operator==(const iterator& it) { return false; }
  };
  iterator find(const std::string& str) {
    return GetBondIndex(str.c_str(), str.size());
  }
  iterator end() const { return iterator(); }

 public:  // 调试支持接口
  void Dump() {}
};
